package switcherXorm

import (
	"database/sql"
	"encoding/json"
	"log"
	"strings"
	"time"

	"ginbase/switcher/comm"

	"github.com/gin-gonic/gin"
	"github.com/xormplus/xorm"
)

// sql执行管道基本信息
type SQLPipeLineBasic struct {
    Sqlstr  string              // sql语句
    Vals    string              // sql语句中从参数
    UUID    string              // 唯一编号 UUID
}

// 执行insert update delete的sql管道
type SQLPipeLineSingle struct {
    SQLPipeLineBasic            // sql执行管道基本信息
    Result  chan *SQLPipeRtn    // 执行结果的channel
}

// 分组执行insert update delete的sql管道
type SQLPipeLineGroup struct {
    Sqls    []*SQLPipeLineBasic // sql执行管道基本信息数组
    Result  chan *SQLPipeRtn    // 执行结果的channel
    UUID    string              // 唯一编号 UUID
}

// sql执行返回结果
type SQLPipeRtn struct {
    Result  bool                // 执行结果 success | false
    Msg     string              // 返回消息
    Rowid   int64               // 影响的行号
    UUID    string              // 唯一编号 UUID
}

// 单个sql执行的channel
var singlePipe chan *SQLPipeLineSingle = make(chan *SQLPipeLineSingle, 10)
// 多个一组的sql执行的channel
var groupPipes chan *SQLPipeLineGroup = make(chan *SQLPipeLineGroup, 5)
// 一大群sql执行的channel
var multiPipes chan *SQLPipeLineSingle = make(chan *SQLPipeLineSingle, 10)


// 排队执行数据库io的子线程
func PipeExecSQL(db *xorm.Engine) {
    log.Println("PipeExecSQL 执行insert update delete的单独子线程开启")
    // 初始化同时执行sql的数组
    sqlArr := make([]*SQLPipeLineSingle, 0)
    for {
        select {
            // 收到单独执行sql的channel数据
        case s := <- singlePipe:
            log.Printf("收到的singlePipe数据 sql:%s, vals:%s, UUID:%s", s.Sqlstr, s.Vals, s.UUID)
            // 调用执行单独sql的函数
            execSingleSQL(s, db)
            // 收到整组执行sql的channel数据
        case g := <- groupPipes:
            log.Printf("收到的groupPipe数据 UUID:%s, sql语句数量:%d", g.UUID, len(g.Sqls))
            // 调用整组执行sql的函数
            execGroupSQL(g, db)
            // 收到同时执行sql的channel数据
        case m := <- multiPipes:
            log.Printf("收到的multiPipes数据 sql:%s, vals:%s, UUID:%s", m.Sqlstr, m.Vals, m.UUID)
            // sql放入数组中 让他串行化执行
            sqlArr = append(sqlArr, m)
            // 每隔一秒钟处理一次同时执行的sql
        case <- time.After(time.Second * 1):
            // 有需要执行的sql
            if len(sqlArr) > 0 {
                log.Println("有需要执行的sql 开始执行")
                // 执行多条sql
                err := execMultiSQLs(sqlArr, db)
                if nil != err {
                    log.Println("本次执行异常", err)
                }
                // 清空sql数组
                sqlArr = make([]*SQLPipeLineSingle, 0)
            }
        }
    }
}

// Dispatch 根据cmd分发到不同的接口函数
func DB4webDispatch(db *xorm.Engine) Xl {
    return Xl{
        "test": func(c *gin.Context) (string, interface{}) {
            id := c.Query("id")
            return "test success", "id=" + id
        },

        // 查询多条select
        "multiFetch": func(c *gin.Context) (string, interface{}) {
            var kvs []MultiSQL
            // 解析多条select的请求参数
            var d = []byte(comm.GetParamWithPanic(c, "multi"))
            err := json.Unmarshal(d, &kvs)
            comm.Perror(err, "无法转换为json")
            ret := make(map[string]interface{})
            // 遍历每一条需要查询的sql
            for _, kv := range kvs {
                log.Printf("multiFetch key:%s, sqlstr:%s, valstr:%s", kv.Key, kv.SQL, kv.Vals)
                // 如果vals不存在，则会被Unmarshal为""
                ret[kv.Key], _ = GetRowDataBySQLStr(kv.SQL, kv.Vals, db)
            }
            return "获取信息成功", ret
        },

        // 查询单条select
        "fetch": func(c *gin.Context) (string, interface{}) {
            var err error
            var list []map[string]string
            // 待执行的sql/sqlmap的key
            sqlstr := comm.GetParamWithPanic(c, "sql")
            // where条件中的参数
            valstr := comm.GetParam(c, "vals")
            // 类型 不填默认按照原始的fetch执行 填了sqlmap则把sqlstr当成key valstr当成参数 去sqlmap中查找配置的静态sql执行
            typestr := comm.GetParam(c, "type")
            log.Printf("fetch sqlstr:%s, valstr:%s, typestr:%s", sqlstr, valstr, typestr)
            // type=sqlmap 以sqlstr作为key valstr作为参数 在sqlmap的配置文件中查找对应的静态sql执行
            if "sqlmap" == typestr {
                list, err = GetRowDataBySQLMap(sqlstr, valstr, db)
            } else {
                // type不是sqlmap 按照旧的fetch直接把sqlstr作为需要执行的sql
                list, err = GetRowDataBySQLStr(sqlstr, valstr, db)
            }
            comm.Perror(err, "查询单条select失败")
            return "查询单条select成功", list
        },

        // 执行单条insert update delete
        // 部分数据库 如postgresql,sql server 不能通过这个接口获得lasteffectId 需要使用fetch接口 并在insert语句中加些东西
        "exec": func(c *gin.Context) (string, interface{}) {
            // 需要执行的sql
            sql := comm.GetParamWithPanic(c, "sql")
            // sql中的参数
            vals := comm.GetParam(c, "vals")
            log.Printf("需要执行的sql:%s, sql中的参数vals:%s", sql, vals)
            // 执行结果的返回触发结构 通过channel传递返回参数到主线程
            resultTrigger := make(chan *SQLPipeRtn)
            // 新建需要执行的sql 管道结构体
            execPipe := newSQLPipeLineSingle(sql, vals, resultTrigger)
            // 管道结构体通过channel传递到专门子线程中
            singlePipe <- execPipe
            // sql执行结果
            var rtn *SQLPipeRtn
            for {
                // 循环检查 是否有返回结果
                select {
                    // 执行结果返回
                case rtn = <- resultTrigger:
                    // 执行超时
                case <- time.After(time.Second * 3):
                    // 手动设置执行超时的返回结果
                    rtn = &SQLPipeRtn{Result: false, Msg: "执行超时", UUID: execPipe.UUID, Rowid: 0}
                }
                log.Println("跳出循环")
                // 跳出循环
                break
            }
            // 判断是否执行失败
            if !rtn.Result {
                log.Println(rtn.Msg)
                panic("sql执行失败," + rtn.Msg)
            }
            // 返回执行结果
            return "sql执行成功", rtn.Rowid
        },

        // 执行一组insert update delete
        "transaction": func(c *gin.Context) (string, interface{}) {
            // 收到的sql json字符串
            sqlstr := comm.GetParamWithPanic(c, "sqls")
            // 执行结果的返回触发结构 通过channel传递返回参数到主线程
            resultTrigger := make(chan *SQLPipeRtn)
            // 解析json字符串并返回一组单条sql执行管道结构体
            pipes, err := getGroupPipeLines([]byte(sqlstr))
            comm.Perror(err, "数据生成解析错误")
            // 新建分组执行sql的管道结构体
            groupExecPipe := newGroupPipeLine(pipes, resultTrigger)
            // 管道结构体通过channel传递到专门子线程中
            groupPipes <- groupExecPipe
            // sql执行结果
            var rtn *SQLPipeRtn
            for {
                // 循环检查 是否有返回结果
                select {
                    // 执行结果返回
                case rtn = <- resultTrigger:
                    // 执行超时
                case <- time.After(time.Second * 3):
                    // 手动设置执行超时的返回结果
                    rtn = &SQLPipeRtn{Result: false, Msg: "执行超时", UUID: groupExecPipe.UUID, Rowid: 0}
                }
                log.Println("跳出循环")
                // 跳出循环
                break
            }
            // 判断是否执行失败
            if !rtn.Result {
                log.Println(rtn.Msg)
                panic("sql分组执行失败," + rtn.Msg)
            }
            // 返回执行结果
            return "sql分组执行成功", nil
        },

        // 同时执行多条insert update delete 把全部要执行的sql以1秒钟一组组成数组 顺序执行
        "multiExec": func(c *gin.Context) (string, interface{}) {
            var sqls []string
            // 解析json字符串中的sql语句
            err := json.Unmarshal([]byte(comm.GetParamWithPanic(c, "sqls")), &sqls)
            comm.Perror(err, "无法转换json")
            // 执行结果的返回触发结构 通过channel传递返回参数到主线程
            resultTrigger := make(chan *SQLPipeRtn)
            // 循环遍历sql语句 使其串行化添加到channel中
            for _, s := range sqls {
                // 新建需要执行的sql 管道结构体
                execPipe := newSQLPipeLineSingle(s, "", resultTrigger)
                // 管道结构体通过channel传递到专门子线程中
                multiPipes <- execPipe
            }
            // 无论是否执行成功全部返回 因为全部transaction的sql都会被扔到同一个数组中 每秒钟分批执行
            // 无法分辨哪一次调用transaction执行成功了 哪一次没成功
            return "事务添加成功", nil
        },
    }
}

// 新建sql执行管道结构体
func newSQLPipeLineBasic(sql, vals string) *SQLPipeLineBasic {
    var s SQLPipeLineBasic
    s.Sqlstr = sql
    s.Vals = vals
    s.UUID = comm.GetUUIDV4()
    return &s
}

// 新建sql执行管道结构体
func newSQLPipeLineSingle(sql, vals string, result chan *SQLPipeRtn) *SQLPipeLineSingle {
    var s SQLPipeLineSingle
    s.Sqlstr = sql
    s.Vals = vals
    s.Result = result
    s.UUID = comm.GetUUIDV4()
    return &s
}

// 新建分组sql执行管道结构体
func newGroupPipeLine(pipes []*SQLPipeLineBasic, result chan *SQLPipeRtn) *SQLPipeLineGroup {
    var s SQLPipeLineGroup
    s.Sqls = pipes
    s.Result = result
    s.UUID = comm.GetUUIDV4()
    return &s
}

// 执行单条sql
func execSingleSQL(s *SQLPipeLineSingle, db *xorm.Engine) {
    // 调用执行sql的函数
    rowid, err := ExecSQLWithWhere(s.Sqlstr, s.Vals, db)
    // 执行结果的结构
    var rtn SQLPipeRtn
    rtn.Rowid = rowid
    rtn.UUID = s.UUID
    // 判断执行失败
    if nil != err {
        log.Println(err)
        rtn.Result = false
        rtn.Msg = err.Error()
    } else {
        rtn.Result = true
    }
    // 执行结果通过channel传递返回
    s.Result <- &rtn
}

// 执行一组sql
func execGroupSQL(g *SQLPipeLineGroup, db *xorm.Engine) {
    log.Printf("整组sql执行开始 UUID:%s", g.UUID)
    var err error
    // 执行结果的结构
    var rtn SQLPipeRtn
    rtn.UUID = g.UUID
    // 开始事务
    tx := db.NewSession()
    defer tx.Close()
    // 异常情况下回滚
    if nil != err {
        log.Println(err)
        tx.Rollback()
        rtn.Result = false
        rtn.Msg = "执行失败," + err.Error()
        // 执行结果通过channel传递返回
        g.Result <- &rtn
    }
    // 遍历需要执行的sql
    for i, s := range g.Sqls {
        log.Printf("执行第%d条sql sql:%s, vals:%s, UUID:%s, groupUUID:%s", i+1, s.Sqlstr, s.Vals, s.UUID, g.UUID)
        // 调用执行sql的函数
        rowid, err := ExecSQLWithWhereSession(s.Sqlstr, s.Vals, tx)
        // 判断执行失败
        if nil != err {
            log.Printf("sql执行失败 sql:%s", s.Sqlstr)
            log.Println(err)
            tx.Rollback()
            rtn.Result = false
            rtn.Msg = "执行失败," + err.Error()
            break
        } else {
            log.Printf("执行第%d条执行成功 sql:%s, rowid:%d", i+1, s.Sqlstr, rowid)
            rtn.Result = true
        }
    }
    // 提交事务
    tx.Commit()
    // 执行结果通过channel传递返回
    g.Result <- &rtn
}

// 执行多条sql
func execMultiSQLs(sqlArr []*SQLPipeLineSingle, db *xorm.Engine) error {
    log.Println("execMultiSQLs 开始")
    var err error
    // 开始事务
    tx := db.NewSession()
    defer tx.Close()
    // 异常情况下回滚
    if nil != err {
        log.Println(err)
        tx.Rollback()
        return err
    }
    // 遍历需要执行的sql
    for i, s := range sqlArr {
        log.Printf("执行第%d条sql sql:%s, vals:%s, UUID:%s", i+1, s.Sqlstr, s.Vals, s.UUID)
        // 调用执行sql的函数
        rowid, err := ExecSQLWithWhereSession(s.Sqlstr, s.Vals, tx)
        // 判断执行失败
        if nil != err {
            log.Printf("sql执行失败 sql:%s", s.Sqlstr)
            log.Println(err)
            tx.Rollback()
            return err
        } else {
            log.Printf("执行第%d条执行成功 sql:%s, rowid:%d", i+1, s.Sqlstr, rowid)
        }
    }
    // 提交事务
    tx.Commit()
    return nil
}


// 解析json字符串
// 判断是单纯的sql字符串数组 还是 有sql和vals构成的结构体数组
// 生成sql执行管道数组
func getGroupPipeLines(jsonstr []byte) ([]*SQLPipeLineBasic, error) {
    var list []*SQLPipeLineBasic
    var err error
    // 单纯的sql语句数组
    var sqls []string
    // sql语句+sql参数的数组
    var multiSQLs []*MultiSQL
    // 是否是单纯的sql语句
    var isSqlString bool
    // 解析json字符串中的sql语句 按照sql语句的数组
    err = json.Unmarshal(jsonstr, &sqls)
    if nil != err {
        // 按照sql语句+参数的数组解析json字符串
        err = json.Unmarshal(jsonstr, &multiSQLs)
        if nil != err {
            log.Println(err)
            return list, err
        }
        // 是sql语句+参数的数组
        isSqlString = false
        log.Println("带sql和vals的结构体数组", string(jsonstr))
    } else {
        isSqlString = true
        log.Println("纯sql的字符串数组", string(jsonstr))
    }
    // 判断是单纯sql语句数组
    if isSqlString {
        // 根据数组元素数量初始化单条sql执行管道的数组
        list = make([]*SQLPipeLineBasic, len(sqls))
        // 循环遍历sql语句
        for i, s := range sqls {
            // 新建执行单条sql的 管道结构体
            singlePipe := newSQLPipeLineBasic(s, "")
            list[i] = singlePipe
        }
    } else {
        // 判断不是单纯sql语句数组
        // 根据数组元素数量初始化单条sql执行管道的数组
        list = make([]*SQLPipeLineBasic, len(multiSQLs))
        // 循环遍历sql语句
        for i, s := range multiSQLs {
            // 新建执行单条sql的 管道结构体
            singlePipe := newSQLPipeLineBasic(s.SQL, s.Vals)
            list[i] = singlePipe
        }
    }
    return list, nil
}

// ================================
// 实际执行的sql函数部分
// ================================
// 执行单条查询语句
func GetRowDataBySQLStr(sqlstr, valstr string, db *xorm.Engine) ([]map[string]string, error) {
    var err error
    var list []map[string]string
    // 解析 sql where 条件
    var vals []interface{}
    err = json.Unmarshal([]byte(valstr), &vals)
    if nil != err {
        log.Println(err)
        return list, err
    }
    // 执行sql语句
    if 0 == len(vals) {
        list, err = db.SQL(sqlstr).QueryString()
    } else {
        list, err = db.SQL(sqlstr, vals...).QueryString()
    }
    return list, err
}

// 执行单条查询语句 通过sqlmap
func GetRowDataBySQLMap(sqlmapkey, valstr string, db *xorm.Engine) ([]map[string]string, error) {
    var err error
    var list []map[string]string
    // 解析 sql where 条件
    var vals []interface{}
    err = json.Unmarshal([]byte(valstr), &vals)
    if nil != err {
        log.Println(err)
        return list, err
    }
    if 0 == len(vals) {
        list, err = db.SqlMapClient(sqlmapkey).QueryString()
    } else {
        list, err = db.SqlMapClient(sqlmapkey, vals...).QueryString()
    }
    return list, err
}

// 执行单条insert update delete语句
func ExecSQLWithWhere(sqlstr, valstr string, db *xorm.Engine) (int64, error) {
    var err error
    var rowid int64
    var result sql.Result
    log.Printf("ExecSQLWithWhere sqlstr:%s, valstr:%s", sqlstr, valstr)
    // 解析参数
    var vals []interface{}
    err = json.Unmarshal([]byte(valstr), &vals)
    if nil != err {
        log.Println(err)
        return rowid, err
    }
    // 执行sql
    if 0 == len(vals) {
        result, err = db.SQL(sqlstr).Execute()
    } else {
        result, err = db.SQL(sqlstr, vals...).Execute()
    }
    if nil != err {
        log.Println(err)
        return rowid, err
    }
    if strings.Index(sqlstr, "insert") >= 0 {
        // insert
        rowid, err = result.LastInsertId()
    } else { 
        // update or detete
        rowid, err = result.RowsAffected()
    }
    return rowid, err
}

// 执行单条insert update delete语句
func ExecSQLWithWhereSession(sqlstr, valstr string, tx *xorm.Session) (int64, error) {
    var err error
    var rowid int64
    var result sql.Result
    log.Printf("ExecSQLWithWhere sqlstr:%s, valstr:%s", sqlstr, valstr)
    // 解析参数
    var vals []interface{}
    err = json.Unmarshal([]byte(valstr), &vals)
    if nil != err {
        log.Println(err)
        return rowid, err
    }
    // 执行sql
    if 0 == len(vals) {
        result, err = tx.SQL(sqlstr).Execute()
    } else {
        result, err = tx.SQL(sqlstr, vals...).Execute()
    }
    if nil != err {
        log.Println(err)
        return rowid, err
    }
    if strings.Index(sqlstr, "insert") >= 0 {
        // insert
        rowid, err = result.LastInsertId()
    } else { 
        // update or detete
        rowid, err = result.RowsAffected()
    }
    return rowid, err
}
// ================================
// 实际执行的sql函数部分
// ================================